#include "rrtracer.h"

RRTracer::RRTracer()
{
    map_index = 0;
}

void RRTracer::rrBeginObject()
{
    objset[map_index] = new RRObject;
}

void RRTracer::rrBeginComponent(RRMaterial *arg)
{
    objset[map_index]->rrBeginComponent(arg);
}

void RRTracer::rrSetPrim(RRPrim *arg)
{
    objset[map_index]->rrSetPrim(arg);
}

void RRTracer::rrEndComponent()
{
    objset[map_index]->rrEndComponent();
}

void RRTracer::rrEndObject()
{
    map_index++;
}

void RRTracer::rrCalcShadow(RRCrossInfo *arg)
{
    RRVec3 cross = arg->rrGetCross();
    RRVec3 l_c = light.rrGetCoord();
    RRVec3 L;
    L = RRSetVec3(l_c.x - cross.x, l_c.y - cross.y, l_c.z - cross.z);
    L = RRNormalize(L);
    RRCrossInfo cr;
    cr.rrSetHit(false);
    RRRay ray;
    ray.rrSetSrc(cross);
    ray.rrSetDir(L);
    rrConfirmHit(&ray, &cr);
    if(cr.rrGetHit() == true) {
        //RRVec3 cross2 = cr.rrGetCross();
        double d1, d2;
        d1 = RRDistance(cross, l_c);
        //d2 = RRDistance(cross, cross2);
        d2 = cr.rrGetRange().s;
        //printf("%lf\n", d2);
        if(d2 < d1)
            arg->rrSetColor(RRSetVec4(0.0, 0.0, 0.0, 0.5));
        else
            arg->rrSetColor(RRSetVec4(1.0, 1.0, 1.0, 0.5));
    }
    else {
        arg->rrSetColor(RRSetVec4(1.0, 1.0, 1.0, 0.5));
    }
    return;
}

void RRTracer::rrCalcReflection(RRCrossInfo *arg)
{
    RRVec3 N;
    N = arg->rrGetNorm();
    N = RRNormalize(N);
    RRVec3 ts;
    ts = arg->rrGetToSrc();
    float coeff;
    coeff = 2 * RRInnerProduct(ts, N);
    RRVec3 R;
    R = RRSetVec3(coeff * N.x - ts.x, coeff * N.y - ts.y, coeff * N.z - ts.z);
    RRRay r;
    r.rrSetSrc(arg->rrGetCross());
    r.rrSetDir(R);

    RRCrossInfo cr;
    rrTrace(&r, &cr);
    arg->rrSetColor(cr.rrGetColor());
    return;
}

void RRTracer::rrCalcDiffusion(RRCrossInfo *arg)
{
    RRVec3 N;
    N = arg->rrGetNorm();
    N = RRNormalize(N);
    RRMaterial m;
    m = arg->rrGetMaterial();
    RRVec4 c;
    c = m.rrGetDiff();
    RRVec3 cross;
    cross = arg->rrGetCross();
    RRVec3 l_c;
    l_c = light.rrGetCoord();
    RRVec3 L;
    L = RRSetVec3(l_c.x - cross.x, l_c.y - cross.y, l_c.z - cross.z);
    L = RRNormalize(L);
    float D;
    D = RRInnerProduct(N, L);
    D = m.rrGetRev() * D;
    if(D < 0.0) D = 0.0;
    c = RRSetVec4(c.x * D, c.y * D, c.z * D, 1.0);
    arg->rrSetColor(c);
}

void RRTracer::rrConfirmHit(RRRay *ray, RRCrossInfo *arg)
{
    RRObject *obj;
    RRCrossInfo cr, cr_temp;
    cr.rrSetHit(false);
    cr_temp.rrSetHit(false);
    RRRange rng, rng_temp;
    rng.s = 1.0e+18;
    map<int, RRObject*>::iterator it = objset.begin();
    while(it != objset.end()) {
        obj = (*it).second;
        obj->rrTrace(ray, &cr_temp);
        if(cr_temp.rrGetHit() == true) {
            rng_temp = cr_temp.rrGetRange();
            if(rng_temp.s < rng.s) {
                rng.s = rng_temp.s;
                cr.rrSetCrossInfo(&cr_temp);
            }
        }
        it++;
    }
    if(cr.rrGetHit() == true)
        arg->rrSetCrossInfo(&cr);
    else
        arg->rrSetCrossInfo(&cr);
    return;
}

void RRTracer::rrTrace(RRRay *ray, RRCrossInfo *arg)
{
    RRObject *obj;
    RRCrossInfo cr, cr_temp;
    cr.rrSetHit(false);
    RRRange rng, rng_temp;
    rng.s = 1.0e+18;
    map<int, RRObject*>::iterator it = objset.begin();
    while(it != objset.end()) {
        obj = (*it).second;
        obj->rrTrace(ray, &cr_temp);
        if(cr_temp.rrGetHit() == true) {
            rng_temp = cr_temp.rrGetRange();
            if(rng_temp.s < rng.s) {
                rng.s = rng_temp.s;
                cr.rrSetCrossInfo(&cr_temp);
            }
        }
        it++;
    }
    if(cr.rrGetHit() == true) {
        RRCrossInfo cr_dif;
        cr_dif.rrSetCrossInfo(&cr);
        rrCalcDiffusion(&cr_dif);
        RRVec4 c_dif = cr_dif.rrGetColor();

        RRCrossInfo cr_shd;
        cr_shd.rrSetCrossInfo(&cr);
        rrCalcShadow(&cr_shd);
        RRVec4 c_shd = cr_shd.rrGetColor();

        double co_ref;
        co_ref = c_dif.w + c_shd.w;
        RRVec4 c_ret;
        c_ret = RRSetVec4((c_dif.x * c_dif.w + c_shd.x * c_shd.w) / co_ref, (c_dif.y * c_dif.w + c_shd.y * c_shd.w) / co_ref, (c_dif.z * c_dif.w + c_shd.z * c_shd.w) / co_ref, 1.0);

        if((co_ref = cr.rrGetMaterial().rrGetRef()) > 0.0) {
            RRCrossInfo cr_ref;
            cr_ref.rrSetCrossInfo(&cr);
            rrCalcReflection(&cr_ref);
            RRVec4 c_ref = cr_ref.rrGetColor();
            c_ret = RRSetVec4(c_ret.x * (1.0 - co_ref) + c_ref.x * co_ref, c_ret.y * (1.0 - co_ref) + c_ref.y * co_ref, c_ret.z * (1.0 - co_ref) + c_ref.z * co_ref, 1.0);
            RRClampVec4(&c_ret, 0.0, 1.0);
            arg->rrSetColor(c_ret);
            return;
        }
        arg->rrSetColor(c_ret);
        return;
    }
    else {
        RRVec4 c;
        c = RRSetVec4(0.0, 0.0, 0.0, 1.0);
        return;
    }
}

RRVec4 RRTracer::rrEmitRay(float xs, float ys)
{
    eye.rrGenerateEye(RRSetVec3(xs, ys, eye.rrGetScreenPos()));
    RRRay r;
    r.rrSetSrc(eye.rrGetEyeCoord());
    r.rrSetDir(eye.rrGetEyeDir());
    RRCrossInfo cr;
    rrTrace(&r, &cr);
    return cr.rrGetColor();
}
